package nl.utwente.eemcs.decomposition.transformation;

import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;

import nl.utwente.eemcs.graph.CommunicatorNode;
import nl.utwente.eemcs.graph.CommunicatorType;
import nl.utwente.eemcs.graph.Edge;
import nl.utwente.eemcs.graph.EdgeType;
import nl.utwente.eemcs.graph.Graph;
import nl.utwente.eemcs.graph.GraphCollaboration;
import nl.utwente.eemcs.graph.Node;
import nl.utwente.eemcs.graph.distribution.DistributionList;


public class DataEdgeValidator {
	@SuppressWarnings("unused")
   private DistributionList distroList;
	private GraphCollaboration collaboration;
	private HashSet<Graph> allGraphs;
	
	private DataEdgeValidator(GraphCollaboration collaboration, DistributionList distroList) {
		this.collaboration = collaboration;
		this.distroList = distroList;
		this.allGraphs = new HashSet<Graph>();
		allGraphs.addAll(collaboration.getProcesses());
	}
	
	public static void analyze(GraphCollaboration collaboration, DistributionList distroList) {
		DataEdgeValidator validator = new DataEdgeValidator(collaboration, distroList);
		validator.validate();
	}
	
	private void validate() {
		// Collect all the data items
		HashSet<String> dataItems = collectDataItems();
		
		for (String dataItem : dataItems) {
			// Get all the data items
			@SuppressWarnings("unused")
         HashSet<Edge> dataEdges = getDataEdgesForDataItem(dataItem);
			System.out.println("Validate");
		}
	}
	
	@SuppressWarnings("unused")
   private void walkGraph(Node node, Graph graph, HashSet<Edge> dataEdges) {
		// Remove edges
		removeEdgesThatEndInNode(node, dataEdges);
		
	}
	
	private void removeEdgesThatEndInNode(Node n, HashSet<Edge> dataEdges) {
		HashSet<Edge> matches = new HashSet<Edge>();
		for (Edge e : dataEdges)
			if (e.getEnd() == n)
				matches.add(e);

		for (Edge e : matches)
			dataEdges.remove(e);
	}
	
	private HashSet<String> collectDataItems() {
		HashSet<Edge> dataEdges = collectAllDataEdges();
		HashSet<String> dataItems = new HashSet<String>();
		for (Edge e : dataEdges)
			dataItems.add(e.getLabel());
		return dataItems;
	}
	
	private HashSet<Edge> getDataEdgesForDataItem(String dataItem) {
		HashSet<Edge> dataEdges = new HashSet<Edge>();
		for (Edge e : collectAllDataEdges())
			if (e.getLabel().equals(dataItem))
				dataEdges.add(e);
		return dataEdges;
	}
	
	/**
	 * Collects all the data edges that were defined
	 * @return List with all the data edges
	 */
	private HashSet<Edge> collectAllDataEdges() {
		HashSet<Edge> edges = new HashSet<Edge>();
		edges.addAll(getDataEdges(collaboration.getMainProcess()));
		for (Graph g : collaboration.getProcesses())
			edges.addAll(getDataEdges(g));
		return edges;
	}
	
	/**
	 * Get the data edges within a graph
	 * @param g Graph
	 * @return List with data edges
	 */
	private List<Edge> getDataEdges(Graph g) {
		List<Edge> result = new ArrayList<Edge>();
		for (Graph subGraph : g.getAllGraphsAndSubGraphs())
			result.addAll(subGraph.getEdges(EdgeType.Data));
		return result;
	}
	
	/**
	 * Find out in which graph the node is defined
	 * @param n Node
	 * @return The graph in which the node is used
	 */
	@SuppressWarnings("unused")
   private Graph nodeInWhichGraph(Node n) {
		for (Graph mainGraph : allGraphs) {
			for (Graph subGraph : mainGraph.getAllGraphsAndSubGraphs())
				if (subGraph.getNodes().contains(n))
					return mainGraph;
		}
		return null;
	}
	
	/**
	 * Get the communicator nodes within a certain graph
	 * @param process Graph
	 * @return
	 */
	private HashSet<CommunicatorNode> getCommunicators(Graph process) {
		HashSet<CommunicatorNode> result = new HashSet<CommunicatorNode>();

		for (Graph subGraphs : process.getAllGraphsAndSubGraphs())
			for (Node n : subGraphs.getNodesByType(CommunicatorNode.class))
				result.add((CommunicatorNode)n);
			
		
		return result;
	}
	
	/**
	 * Get communicators within a certain graph with a CommunicatorType
	 * @param process Graph
	 * @param type CommunicatorType
	 * @return
	 */
	@SuppressWarnings("unused")
   private HashSet<CommunicatorNode> getCommunicators(Graph process, CommunicatorType... type) {
		HashSet<CommunicatorNode> result = new HashSet<CommunicatorNode>();
		for (CommunicatorNode n : getCommunicators(process))
			for (CommunicatorType t : type)
				if (t == n.getType())
					result.add(n);
		return result;
	}
	
	/**
	 * Get the communicator that communicates with a particular communicator
	 * @param node Communicator
	 * @param communicationEdges The communication edges list
	 * @return
	 */
	@SuppressWarnings("unused")
   private CommunicatorNode getMatchingCommunicator(CommunicatorNode node, List<Edge> communicationEdges) {
		for (Edge e : communicationEdges) {
			if (e.getEdgeType() == EdgeType.Communication && e.getStart() == node)
				return (CommunicatorNode)e.getEnd();
			else if (e.getEdgeType() == EdgeType.Communication && e.getEnd() == node)
				return (CommunicatorNode)e.getStart();
		}
		return null;
	}	
}